#ifndef SERIAL_C842A73D_5F24_4C15_9E5F_6B8B7C6A1DD9
#define SERIAL_C842A73D_5F24_4C15_9E5F_6B8B7C6A1DD9

#include <cassert>
#include <climits>
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>

// 平台检测
#if defined(_WIN32) || defined(_WIN64)
#include <synchapi.h>
#include <windows.h>
#include <winsock2.h>


#elif defined(__linux__) || defined(__APPLE__)
#define SERIAL_UNIX
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#else
#error "Unsupported platform"
#endif

class IOC_Serial {
public:
    /**
     * @brief 串口数据位长度
     *
     */
    enum class SerialDataBit {
        SERIAL_DATABIT_5 = 5,
        SERIAL_DATABIT_6,
        SERIAL_DATABIT_7,
        SERIAL_DATABIT_8,
    };

    /**
     * @brief 串口停止位长度
     *
     */
    enum class SerialStopBit { SERIAL_STOPBIT_1, SERIAL_STOPBIT_1_5, SERIAL_STOPBIT_2 };

    /**
     * @brief 串口校验类型
     *
     */
    enum class SerialParity {
        SERIAL_PARITY_NONE,
        SERIAL_PARITY_ODD,
        SERIAL_PARITY_EVEN,
        SERIAL_PARITY_MARK,
        SERIAL_PARITY_SPACE
    };

    /**
     * @brief 串口错误类型
     *
     */
    enum class SerialError {
        SERIAL_OPEN_ERROR = -1,
        SERIAL_BAUD_ERROR = -2,
        SERIAL_PARITY_ERROR = -3,
        SERIAL_DATABIT_ERROR = -4,
        SERIAL_STOPBIT_ERROR = -5,
        SERIAL_RTS_DTR_ERROR = -6
    };

    IOC_Serial() = default;
    /**
     * @brief Construct a new IOC_Serial:: IOC_Serial object
     *
     * @param serial_name 串口名，如/dev/tty
     * @param baudrate 波特率
     * @param serial_databit 数据位长度
     * @param serial_parity 校验位类型
     * @param serial_stopbit 停止位长度
     * @param rts RTS
     * @param dtr DTR
     * @param timeout_ms 超时时间
     */
    IOC_Serial(std::string serial_name, const unsigned int baudrate,
           SerialDataBit serial_databit = SerialDataBit::SERIAL_DATABIT_8,
           SerialParity serial_parity = SerialParity::SERIAL_PARITY_NONE,
           SerialStopBit serial_stopbit = SerialStopBit::SERIAL_STOPBIT_1,
           bool rts = false, bool dtr = false, unsigned int timeout_ms = 0) {
        assert(serial_name.size() > 0 && "串口名为空");
        assert(baudrate > 0 && "波特率不能小于0");

#ifdef _WIN32
        // Windows串口名需要加上"\\\\.\\""前缀
        this->__serial_name = "\\\\.\\" + serial_name;
#else
        this->__serial_name = serial_name;
#endif
        this->__baudrate = baudrate;
        this->__data_bits = serial_databit;
        this->__parity = serial_parity;
        this->__stop_bits = serial_stopbit;
        this->__timeout_ms = timeout_ms;
        this->__rts = rts;
        this->__dtr = dtr;
        __open_port();
    }


    /**
     * @brief 关闭串口
     * 
     * @return true 成功
     * @return false 失败
     */
    bool close_port() {
#ifdef _WIN32
        if (is_open()) {
            CloseHandle(this->__handle);
            this->__handle = INVALID_HANDLE_VALUE;
            return true;
        }
#else
        if (is_open()) {
            close(this->__fd);
            this->__fd = INT_MIN;
            return true;
        }
#endif
        return false;
    }

    /**
     * @brief 发送字节序列
     * 
     * @param buffer 要发送的字节数组
     * @param size 数组长度
     * @return int 发送的数据长度, <0 表示错误
     */
    int send_bytes(const uint8_t *buffer, int size) {
        if (!is_open()) {
            std::cerr << "串口未开启, 发送数据失败" << std::endl;
            return -1;
        }

#ifdef _WIN32
        DWORD bytes_written;
        if (!WriteFile(this->__handle, buffer, size, &bytes_written, NULL)) {
            return -3;
        }
        return static_cast<int>(bytes_written);
#else
        int length = write(this->__fd, buffer, size);
        if (length != size) {
            return -3;
        }
        return length;
#endif
    }

    /**
     * @brief 发送字节序列
     *
     * @param buffer 要发送的字节数组
     * @return int 发送的数据长度, <0 表示错误
     */
    int send_bytes(const std::vector<uint8_t> &data) {
        return send_bytes(data.data(), data.size());
    }

    /**
     * @brief 接收字节序列
     * 
     * @param buffer 接收缓冲区
     * @param size 缓冲区大小
     * @return int 接收到的数据长度, <0 表示错误
     */
    int recv_bytes(uint8_t *buffer, int size) {
        if (!is_open()) {
            return -1;
        }

#ifdef _WIN32
        DWORD dwErrorFlags;  //错误标志
        COMSTAT comStat;     //通讯状态
        OVERLAPPED m_osRead; //异步输入输出结构体
        DWORD read_bytes;
        //创建一个用于OVERLAPPED的事件处理，不会真正用到，但系统要求这么做
        memset(&m_osRead, 0, sizeof(m_osRead));
        m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, "ReadEvent");

        ClearCommError(__handle, &dwErrorFlags,
                       &comStat);       //清除通讯错误，获得设备当前状态
        if (!comStat.cbInQue) return 0; //如果输入缓冲区字节数为0，则返回false
        //std::cout << comStat.cbInQue << std::endl;
        BOOL bReadStat = ReadFile(__handle, //串口句柄
                                  buffer,   //数据首地址
                                  size,     //要读取的数据最大字节数
                                  &read_bytes, //DWORD*,用来接收返回成功读取的数据字节数
                                  &m_osRead); //NULL为同步发送，OVERLAPPED*为异步发送
        if (!bReadStat) {
            if (GetLastError() == ERROR_IO_PENDING) //如果串口正在读取中
            {
                //GetOverlappedResult函数的最后一个参数设为TRUE
                //函数会一直等待，直到读操作完成或由于错误而返回
                GetOverlappedResult(__handle, &m_osRead, &read_bytes, TRUE);
            } else {
                ClearCommError(__handle, &dwErrorFlags, &comStat); //清除通讯错误
                CloseHandle(m_osRead.hEvent); //关闭并释放hEvent的内存
                return 0;
            }
        }
        return read_bytes;
#else
        ssize_t len = read(this->__fd, buffer, size);
        if (len == -1) {
            if ((errno == EAGAIN || errno == EWOULDBLOCK)) {
                return 0;
            } else {
                this->close_port();
                std::perror("recv error");
            }
        }
        return len;
#endif
    }

    /**
     * @brief 接收字节序列
     * 
     * @param buffer 接收缓冲区
     * @return int 接收到的数据长度, <0 表示错误
     */
    int recv_bytes(std::vector<uint8_t> &buffer) {
        return recv_bytes(buffer.data(), buffer.size());
    }

    /**
     * @brief 检测串口是否可用
     *
     * @return true 已开启
     * @return false 未开启
     */
    bool is_open() {
#ifdef _WIN32
        return this->__handle != INVALID_HANDLE_VALUE;
#else
        return this->__fd != INT_MIN;
#endif
    }

    /**
     * @brief 检测串口中是否有数据
     * 
     * @return true 串口中有数据
     * @return false 串口中无数据
     */
    bool is_available() {
        if (!is_open()) {
            std::cerr << "串口未开启" << std::endl;
            return false;
        }

#ifdef _WIN32
        DWORD errors;
        COMSTAT status;
        ClearCommError(this->__handle, &errors, &status);
        return status.cbInQue > 0;
#else
        int bytes_available;
        ioctl(this->__fd, FIONREAD, &bytes_available);
        return bytes_available > 0;
#endif
    }

    ~IOC_Serial() {
        close_port();
    }

private:
    /**
     * @brief 开启串口
     *
     * @return int 0 成功,<0 失败
     */
    int __open_port() {
#ifdef _WIN32
        if (this->__handle != INVALID_HANDLE_VALUE) {
            return static_cast<int>(IOC_Serial::SerialError::SERIAL_OPEN_ERROR);
        }

        // 以非阻塞方式打开串口
        __handle = CreateFile(__serial_name.c_str(), GENERIC_READ | GENERIC_WRITE,
                              0,             // 不共享
                              NULL,          // 默认安全属性
                              OPEN_EXISTING, // 打开现有的串口
                              FILE_FLAG_OVERLAPPED, NULL);

        if (__handle == INVALID_HANDLE_VALUE) {
            return static_cast<int>(SerialError::SERIAL_OPEN_ERROR);
        }
        if (!SetupComm(__handle, 1024, 1024)) {
            return false;
        }

        // 配置串口参数
        // Structure for the port parameters
        DCB dcbSerialParams;
        dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

        // Get the port parameters
        if (!GetCommState(__handle, &dcbSerialParams)) return -3;

        // Set the speed (Bauds)
        switch (__baudrate) {
            case 110:
                dcbSerialParams.BaudRate = CBR_110;
                break;
            case 300:
                dcbSerialParams.BaudRate = CBR_300;
                break;
            case 600:
                dcbSerialParams.BaudRate = CBR_600;
                break;
            case 1200:
                dcbSerialParams.BaudRate = CBR_1200;
                break;
            case 2400:
                dcbSerialParams.BaudRate = CBR_2400;
                break;
            case 4800:
                dcbSerialParams.BaudRate = CBR_4800;
                break;
            case 9600:
                dcbSerialParams.BaudRate = CBR_9600;
                break;
            case 14400:
                dcbSerialParams.BaudRate = CBR_14400;
                break;
            case 19200:
                dcbSerialParams.BaudRate = CBR_19200;
                break;
            case 38400:
                dcbSerialParams.BaudRate = CBR_38400;
                break;
            case 56000:
                dcbSerialParams.BaudRate = CBR_56000;
                break;
            case 57600:
                dcbSerialParams.BaudRate = CBR_57600;
                break;
            case 115200:
                dcbSerialParams.BaudRate = CBR_115200;
                break;
            case 128000:
                dcbSerialParams.BaudRate = CBR_128000;
                break;
            case 256000:
                dcbSerialParams.BaudRate = CBR_256000;
                break;
            default:
                return -4;
        }
        //select data size
        BYTE bytesize = 0;
        switch (__data_bits) {
            case SerialDataBit::SERIAL_DATABIT_5:
                bytesize = 5;
                break;
            case SerialDataBit::SERIAL_DATABIT_6:
                bytesize = 6;
                break;
            case SerialDataBit::SERIAL_DATABIT_7:
                bytesize = 7;
                break;
            case SerialDataBit::SERIAL_DATABIT_8:
                bytesize = 8;
                break;
            default:
                return -7;
        }
        BYTE stopBits = 0;
        switch (__stop_bits) {
            case SerialStopBit::SERIAL_STOPBIT_1:
                stopBits = ONESTOPBIT;
                break;
            case SerialStopBit::SERIAL_STOPBIT_1_5:
                stopBits = ONE5STOPBITS;
                break;
            case SerialStopBit::SERIAL_STOPBIT_2:
                stopBits = TWOSTOPBITS;
                break;
            default:
                return -8;
        }
        BYTE parity = 0;
        switch (__parity) {
            case SerialParity::SERIAL_PARITY_NONE:
                parity = NOPARITY;
                break;
            case SerialParity::SERIAL_PARITY_EVEN:
                parity = EVENPARITY;
                break;
            case SerialParity::SERIAL_PARITY_ODD:
                parity = ODDPARITY;
                break;
            case SerialParity::SERIAL_PARITY_MARK:
                parity = MARKPARITY;
                break;
            case SerialParity::SERIAL_PARITY_SPACE:
                parity = SPACEPARITY;
                break;
            default:
                return -9;
        }
        // configure byte size
        dcbSerialParams.ByteSize = bytesize;
        // configure stop bits
        dcbSerialParams.StopBits = stopBits;
        // configure parity
        dcbSerialParams.Parity = parity;

        dcbSerialParams.fBinary = TRUE;
        dcbSerialParams.fParity = (__parity != SerialParity::SERIAL_PARITY_NONE);
        // 禁用流控制
        dcbSerialParams.fOutxCtsFlow = FALSE; // 禁用 CTS 输出流控制
        dcbSerialParams.fOutxDsrFlow = FALSE; // 禁用 DSR 输出流控制
        dcbSerialParams.fDtrControl =
                __dtr ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE; // 根据__dtr设置
        dcbSerialParams.fDsrSensitivity = FALSE;                  // 禁用 DSR 敏感性
        dcbSerialParams.fRtsControl =
                __rts ? RTS_CONTROL_ENABLE : RTS_CONTROL_DISABLE; // 根据__rts设置
        dcbSerialParams.fOutX = FALSE; // 禁用 XON/XOFF 输出流控制
        dcbSerialParams.fInX = FALSE;  // 禁用 XON/XOFF 输入流控制

        if (!SetCommState(__handle, &dcbSerialParams)) {
            CloseHandle(__handle);
            return static_cast<int>(SerialError::SERIAL_OPEN_ERROR);
        }
        if (!SetupComm(__handle, 4096, 4096)) {
            CloseHandle(__handle);
            return static_cast<int>(SerialError::SERIAL_OPEN_ERROR);
        }
        // 设置超时
        COMMTIMEOUTS timeouts = {0};
        timeouts.ReadIntervalTimeout = 1;        // 很小的间隔超时
        timeouts.ReadTotalTimeoutMultiplier = 0; // 不使用乘数超时
        timeouts.ReadTotalTimeoutConstant = 1;   // 很小的总超时
        timeouts.WriteTotalTimeoutMultiplier = 0;
        timeouts.WriteTotalTimeoutConstant = 1000; // 写操作给 1 秒超时

        if (!SetCommTimeouts(__handle, &timeouts)) {
            CloseHandle(__handle);
            return static_cast<int>(SerialError::SERIAL_OPEN_ERROR);
        }
        Sleep(100);
        DWORD status;
        if (!GetCommModemStatus(__handle, &status)) {
            CloseHandle(__handle);
            return static_cast<int>(SerialError::SERIAL_OPEN_ERROR);
        }
        // 清空缓冲区
        PurgeComm(__handle, PURGE_RXCLEAR | PURGE_TXCLEAR);

        return 0;
#else
        int fd;
        if (this->__fd != INT_MIN) {
            return static_cast<int>(IOC_Serial::SerialError::SERIAL_OPEN_ERROR);
        }
        // 避免重复开启同一个串口
        fd = open(this->__serial_name.c_str(), O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
        if (fd < 0) {
            std::perror("open_port: Unable to open port");
            return static_cast<int>(IOC_Serial::SerialError::SERIAL_OPEN_ERROR);
        }
        this->__fd = fd;
        // 检查端口状态
        int status;
        if (ioctl(fd, TIOCMGET, &status) < 0) {
            perror("Error checking port status");
            close(fd);
            return static_cast<int>(IOC_Serial::SerialError::SERIAL_OPEN_ERROR);
        }
        usleep(10000);
        // 设置串口独占访问
        if (ioctl(fd, TIOCEXCL) == -1) {
            perror("Error setting port exclusive-use");
            close(fd);
            return static_cast<int>(IOC_Serial::SerialError::SERIAL_OPEN_ERROR);
        }
        struct termios options;
        tcgetattr(fd, &options);
        unsigned int Speed;
        // 配置波特率
        switch (this->__baudrate) {
            case 110:
                Speed = B110;
                break;
            case 300:
                Speed = B300;
                break;
            case 600:
                Speed = B600;
                break;
            case 1200:
                Speed = B1200;
                break;
            case 2400:
                Speed = B2400;
                break;
            case 4800:
                Speed = B4800;
                break;
            case 9600:
                Speed = B9600;
                break;
            case 19200:
                Speed = B19200;
                break;
            case 38400:
                Speed = B38400;
                break;
            case 57600:
                Speed = B57600;
                break;
            case 115200:
                Speed = B115200;
                break;
#if defined(B230400)
            case 230400:
                Speed = B230400;
                break;
#endif
#if defined(B460800)
            case 460800:
                Speed = B460800;
                break;
#endif
#if defined(B500000)
            case 500000:
                Speed = B500000;
                break;
#endif
#if defined(B576000)
            case 576000:
                Speed = B576000;
                break;
#endif
#if defined(B921600)
            case 921600:
                Speed = B921600;
                break;
#endif
#if defined(B1000000)
            case 1000000:
                Speed = B1000000;
                break;
#endif
#if defined(B1152000)
            case 1152000:
                Speed = B1152000;
                break;
#endif
#if defined(B1500000)
            case 1500000:
                Speed = B1500000;
                break;
#endif
#if defined(B2000000)
            case 2000000:
                Speed = B2000000;
                break;
#endif
#if defined(B2500000)
            case 2500000:
                Speed = B2500000;
                break;
#endif
#if defined(B3000000)
            case 3000000:
                Speed = B3000000;
                break;
#endif
#if defined(B3500000)
            case 3500000:
                Speed = B3500000;
                break;
#endif
#if defined(B4000000)
            case 4000000:
                Speed = B4000000;
                break;
#endif
            default:
                return static_cast<int>(IOC_Serial::SerialError::SERIAL_BAUD_ERROR);
        }

        unsigned int databit_flag;
        // 配置数据位长度
        switch (this->__data_bits) {
            case SerialDataBit::SERIAL_DATABIT_5:
                databit_flag = CS5;
                break;
            case SerialDataBit::SERIAL_DATABIT_6:
                databit_flag = CS6;
                break;
            case SerialDataBit::SERIAL_DATABIT_7:
                databit_flag = CS7;
                break;
            case SerialDataBit::SERIAL_DATABIT_8:
                databit_flag = CS8;
                break;
            default:
                return static_cast<int>(IOC_Serial::SerialError::SERIAL_DATABIT_ERROR);
        }

        unsigned int stopbits_flag = 0;
        // 配置停止位长度
        switch (this->__stop_bits) {
            case SerialStopBit::SERIAL_STOPBIT_1:
                stopbits_flag = 0;
                break;
            case SerialStopBit::SERIAL_STOPBIT_2:
                stopbits_flag = CSTOPB;
                break;
            default:
                return static_cast<int>(IOC_Serial::SerialError::SERIAL_STOPBIT_ERROR);
        }

        unsigned int parity_flag = 0;
        // 配置校验位类型
        switch (this->__parity) {
            case SerialParity::SERIAL_PARITY_NONE:
                parity_flag = 0;
                break;
            case SerialParity::SERIAL_PARITY_ODD:
                parity_flag = PARENB | PARODD;
                break;
            case SerialParity::SERIAL_PARITY_EVEN:
                parity_flag = PARENB;
                break;
            default:
                return static_cast<int>(IOC_Serial::SerialError::SERIAL_PARITY_ERROR);
        }

        /* 设置输入波特率 */
        cfsetispeed(&options, Speed);
        /* 设置输出波特率 */
        cfsetospeed(&options, Speed);
        options.c_cflag |= (CLOCAL | CREAD);
        options.c_cflag &= ~CSIZE;
        options.c_cflag |=
                (databit_flag | stopbits_flag | parity_flag); // 设置数据位|校验位|停止位
        options.c_iflag |= (IGNPAR | IGNBRK); // 忽略串口断开和校验错误
        options.c_cc[VTIME] = 1;              // 1秒超时
        options.c_cc[VMIN] = 0;               // 非阻塞读取
        // 设置非规范模式
        options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
        options.c_iflag &= ~(IXON | IXOFF | IXANY);  // 禁用软件流控
        options.c_iflag &= ~(INLCR | ICRNL | IGNCR); // 禁用所有CR/LF处理
        options.c_oflag &= ~OPOST;                   // 禁用输出处理
        tcsetattr(fd, TCSANOW, &options);
        tcflush(fd, TCIOFLUSH); // 清空缓冲区
        // 设置流控
        if (this->__rts) {
            int rts_flag = TIOCM_RTS;
            if (ioctl(fd, TIOCMBIS, &rts_flag) == -1) {
                std::perror("Failed to set RTS");
                return static_cast<int>(IOC_Serial::SerialError::SERIAL_RTS_DTR_ERROR);
            }
        }

        if (this->__dtr) {
            int dtr_flag = TIOCM_DTR;
            if (ioctl(fd, TIOCMBIS, &dtr_flag) == -1) {
                std::perror("Failed to set DTR");
                return static_cast<int>(IOC_Serial::SerialError::SERIAL_RTS_DTR_ERROR);
            }
        }
        usleep(100000); // 等待设备准备好
        return 0;
#endif
    }

private:
    std::string __serial_name;
    unsigned int __baudrate;
    SerialDataBit __data_bits;
    SerialStopBit __stop_bits;
    SerialParity __parity;
    unsigned int __timeout_ms;
    bool __rts;
    bool __dtr;
#ifdef _WIN32
    HANDLE __handle = INVALID_HANDLE_VALUE;
#else
    int __fd = INT_MIN;
#endif
};
#endif /* SERIAL_C842A73D_5F24_4C15_9E5F_6B8B7C6A1DD9 */
